﻿using QuanLyGiaoVuDAL.DTO;
using QuanLyGiaoVuDAL.Repositories.DatabaseRepositories;
using QuanLyGiaoVuService.CommandServices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace QuanLyGiaoVuService.CommonServices
{
    public class XepLich2Service : IXepLich2Service
    {
        private IGiangVienRepository _giangVienRepository;
        private ILopMonHocCommandService _lopMonHocCommandService;
        private ILopMonHocRepository _lopMonHocRepository;
        private const string tooltipStr = "{0} <br />Giảng viên: {1} <br />Số tín chỉ: {2} <br /> Lớp: {3}";

        public XepLich2Service(IGiangVienRepository giangVienRepository, ILopMonHocRepository lopMonHocRepository, ILopMonHocCommandService lopMonHocCommandService)
        {
            _giangVienRepository = giangVienRepository;
            _lopMonHocRepository = lopMonHocRepository;
            _lopMonHocCommandService = lopMonHocCommandService;
        }

        public IEnumerable<LopMonHocDTO> TaoDanhSachKhaNang(Guid maHocKy)
        {
            var danhSachLopMonHoc = _lopMonHocRepository.GetAllLopMonHoc(maHocKy, null, true, true, true).Where(lop => lop.LoaiLop == 0 && lop.GiangVien1.ThinhGiang == false).ToList();
            IList<LopMonHocDTO> resultList = new List<LopMonHocDTO>();
            var danhSachLichRanh = _giangVienRepository.GetAllLichRanh(null, null, null);

            foreach (LopMonHocDTO lopMonHoc in danhSachLopMonHoc)
            {
                lopMonHoc.Bac = danhSachLopMonHoc.Count(lop => (lop.MaGiangVien1 == lopMonHoc.MaGiangVien1 || lop.LopSinhVienId == lopMonHoc.LopSinhVienId) && lop.LopMonHocId != lopMonHoc.LopMonHocId);
                lopMonHoc.DanhSachKhaNang = new List<int>();
                foreach (LichRanhDTO lichRanh in danhSachLichRanh)
                {
                    if (lopMonHoc.MaGiangVien1 == lichRanh.GiangVien)
                    {
                        int thuTu1 = (lichRanh.Thu - 2) * 4 + 1 + (lichRanh.Buoi ? 0 : 2);
                        lopMonHoc.DanhSachKhaNang.Add(thuTu1);
                        if (lopMonHoc.MonHoc.SoTinChiLyThuyet <= 2)
                        {
                            int thuTu2 = (lichRanh.Thu - 2) * 4 + 2 + (lichRanh.Buoi ? 0 : 2);
                            lopMonHoc.DanhSachKhaNang.Add(thuTu2);
                        }
                    }
                }
            }
            return danhSachLopMonHoc;
        }

        public IEnumerable<QuanLyGiaoVuDAL.DTO.LopMonHocDTO> XepLich(Guid maHocKy, out List<LopMonHocDTO> danhSachKhongTheXep, out List<GiangVienDTO> danhSachGVChuaDKLich)
        {
            var danhSachLopMonHoc = _lopMonHocRepository.GetAllLopMonHoc(maHocKy, null, true, true, true).Where(lop => lop.LoaiLop == 0 && lop.GiangVien1.ThinhGiang == false).ToList();
            danhSachKhongTheXep = new List<LopMonHocDTO>();
            IList<LopMonHocDTO> resultList = new List<LopMonHocDTO>();
            var danhSachLichRanh = _giangVienRepository.GetAllLichRanh(null, null, null);

            foreach (LopMonHocDTO lopMonHoc in danhSachLopMonHoc)
            {
                lopMonHoc.Bac = danhSachLopMonHoc.Count(lop => (lop.MaGiangVien1 == lopMonHoc.MaGiangVien1 || lop.LopSinhVienId == lopMonHoc.LopSinhVienId)  && lop.LopMonHocId != lopMonHoc.LopMonHocId);
                lopMonHoc.DanhSachKhaNang = new List<int>();
                foreach (LichRanhDTO lichRanh in danhSachLichRanh)
                {
                    if (lopMonHoc.MaGiangVien1 == lichRanh.GiangVien)
                    {
                        int thuTu1 = (lichRanh.Thu - 2) * 4 + 1 + (lichRanh.Buoi ? 0 : 2);
                        lopMonHoc.DanhSachKhaNang.Add(thuTu1);
                        if (lopMonHoc.MonHoc.SoTinChiLyThuyet <= 2)
                        {
                            int thuTu2 = (lichRanh.Thu - 2) * 4 + 2 + (lichRanh.Buoi ? 0 : 2);
                            lopMonHoc.DanhSachKhaNang.Add(thuTu2);
                        }
                    }
                }
            }

            var dsLopGVChuaDKLich = danhSachLopMonHoc.Where(lop => lop.DanhSachKhaNang.Count == 0 && lop.GiangVien1.ThinhGiang == false);
            danhSachKhongTheXep.AddRange(dsLopGVChuaDKLich);
            danhSachGVChuaDKLich = new List<GiangVienDTO>();
            foreach (var lop in dsLopGVChuaDKLich)
            {
                if (lop.GiangVien1 != null && danhSachGVChuaDKLich.FindIndex( gv => gv.ID == lop.GiangVien1.ID) < 0)
                    danhSachGVChuaDKLich.Add(lop.GiangVien1);
            }

            var dsLopCoTheXepLich = danhSachLopMonHoc.Where(lop => !(lop.DanhSachKhaNang.Count == 0 && lop.GiangVien1.ThinhGiang == false)).ToList();

            int count = dsLopCoTheXepLich.Count();
            for (int index = 0; index < count; index++ )
            {
                var dsLopChuaXet = dsLopCoTheXepLich.Where(lop => lop.DaXet == false);
                if (dsLopChuaXet.Count() == 0)
                    break;
                int maxBac = dsLopChuaXet.Max(lop => lop.Bac);
                LopMonHocDTO lopMonHoc;
                try
                {
                    lopMonHoc = dsLopCoTheXepLich.Where(lop => lop.DaXet == false).First(lop => lop.Bac == maxBac);
                }
                catch
                {
                    break;
                }
                if (lopMonHoc.DanhSachKhaNang.Count() > 0)
                {
                    lopMonHoc.ThuTuXepLich = lopMonHoc.DanhSachKhaNang.First();
                    lopMonHoc.DaXet = true;
                }
                else
                {
                    danhSachKhongTheXep.Add(lopMonHoc);
                    lopMonHoc.DaXet = true;
                    continue;
                }
                foreach (var lop in dsLopCoTheXepLich)
                {
                    if (lop.LopMonHocId != lopMonHoc.LopMonHocId && (lop.MaGiangVien1 == lopMonHoc.MaGiangVien1 || lop.LopSinhVienId == lopMonHoc.LopSinhVienId))
                    {
                        lop.Bac--;
                        if (lop.DanhSachKhaNang.Contains(lopMonHoc.ThuTuXepLich))
                        {
                            lop.DanhSachKhaNang.Remove(lopMonHoc.ThuTuXepLich);
                        }
                        if (lop.MonHoc.SoTinChiLyThuyet <= 2)
                        {
                            if (lop.DanhSachKhaNang.Contains(lopMonHoc.ThuTuXepLich + 1))
                            {
                                lop.DanhSachKhaNang.Remove(lopMonHoc.ThuTuXepLich + 1);
                            }
                        }
                    }
                }
                resultList.Add(lopMonHoc);
            }
            var dsChuaDuocXepLich = dsLopCoTheXepLich.Where(lop => lop.DaXet == false);
            if (dsChuaDuocXepLich != null)
                danhSachKhongTheXep.AddRange(dsChuaDuocXepLich);
            return resultList;
        }

        public string BuildHtml(string lopMonHocIds, string khaNangs)
        {
            List<LopMonHocDTO> danhSachLopMonHoc = _lopMonHocRepository.GetLopMonHocByIdList(lopMonHocIds).ToList();

            string[] khaNangList = khaNangs.Split(',');
            if (danhSachLopMonHoc.Count() != khaNangList.Count())
                return String.Empty;
            int countLMH = danhSachLopMonHoc.Count();
            for (int subIndex = 0; subIndex < countLMH; subIndex++)
            {
                danhSachLopMonHoc[subIndex].KhaNangDuocChon = Convert.ToInt32(khaNangList[subIndex]);
            }
            string message = KiemTraTrungGio(danhSachLopMonHoc);
            if (message != string.Empty)
                throw new Exception(message);

            StringBuilder builder = new StringBuilder();
            int sumRow, row2Ca;
            List<LopMonHocDTO> danhSachDaSapXep = SapXepLopMonHoc(danhSachLopMonHoc, out sumRow, out row2Ca);

            //
            builder.Append("<table>");
            int index = 0;
            while (index < row2Ca * 2)
            {
                builder.Append("<tr>");
                builder.AppendFormat("<td class='chosen-class-item' data-id='{0}' title='{1}'>",danhSachDaSapXep[index].LopMonHocId, String.Format(tooltipStr,danhSachDaSapXep[index].MaLopVaTenMonHoc, danhSachDaSapXep[index].GiangVien1.GetTenVaHocVi(), danhSachDaSapXep[index].MonHoc.TongSoTinChi, danhSachDaSapXep[index].LopSinhVien.TenLop));
                builder.Append(danhSachDaSapXep[index++].MaLopVaTenMonHoc);
                builder.Append("</td>");
                builder.AppendFormat("<td class='chosen-class-item' data-id='{0}' title='{1}'>", danhSachDaSapXep[index].LopMonHocId, String.Format(tooltipStr, danhSachDaSapXep[index].MaLopVaTenMonHoc, danhSachDaSapXep[index].GiangVien1.GetTenVaHocVi(), danhSachDaSapXep[index].MonHoc.TongSoTinChi, danhSachDaSapXep[index].LopSinhVien.TenLop));
                builder.Append(danhSachDaSapXep[index++].MaLopVaTenMonHoc);
                builder.Append("</td>");
                builder.Append("</tr>");
            }
            int count = danhSachDaSapXep.Count;
            while (index < count)
            {
                var lopMonHoc = danhSachDaSapXep[index++];
                if (lopMonHoc.MonHoc.SoTinChiLyThuyet <= 2)
                {
                    builder.Append("<tr>");
                    if (lopMonHoc.KhaNangDuocChon % 2 != 0)
                    {
                        builder.AppendFormat("<td class='chosen-class-item' data-id='{0}' title='{1}'>", lopMonHoc.LopMonHocId, String.Format(tooltipStr, lopMonHoc.MaLopVaTenMonHoc, lopMonHoc.GiangVien1.GetTenVaHocVi(), lopMonHoc.MonHoc.TongSoTinChi, lopMonHoc.LopSinhVien.TenLop));
                        builder.Append(lopMonHoc.MaLopVaTenMonHoc);
                        builder.Append("</td>");
                        builder.Append("<td>");
                        builder.Append("</td>");
                    }
                    else
                    {
                        builder.Append("<td>");
                        builder.Append("</td>");
                        builder.AppendFormat("<td class='chosen-class-item' data-id='{0}' title='{1}'>", lopMonHoc.LopMonHocId, String.Format(tooltipStr, lopMonHoc.MaLopVaTenMonHoc, lopMonHoc.GiangVien1.GetTenVaHocVi(), lopMonHoc.MonHoc.TongSoTinChi, lopMonHoc.LopSinhVien.TenLop));
                        builder.Append(lopMonHoc.MaLopVaTenMonHoc);
                        builder.Append("</td>");
                    }
                }
                else
                {
                    builder.AppendFormat("<td colspan='2' class='chosen-class-item' data-id='{0}' title='{1}'>", lopMonHoc.LopMonHocId, String.Format(tooltipStr, lopMonHoc.MaLopVaTenMonHoc, lopMonHoc.GiangVien1.GetTenVaHocVi(), lopMonHoc.MonHoc.TongSoTinChi, lopMonHoc.LopSinhVien.TenLop));
                    builder.Append(lopMonHoc.MaLopVaTenMonHoc);
                    builder.Append("</td>");
                }
                builder.Append("</tr>");
            }
            builder.Append("</table>");
            return builder.ToString();
        }

        private List<LopMonHocDTO> SapXepLopMonHoc(IEnumerable<LopMonHocDTO> danhSachLopMonHoc, out int row, out int row2Ca)
        {
            List<LopMonHocDTO> danhSachKetQua = new List<LopMonHocDTO>();
            List<LopMonHocDTO> danhSachLopCaChan = danhSachLopMonHoc.Where(lop => lop.MonHoc.SoTinChiLyThuyet <= 2 && (lop.KhaNangDuocChon % 2) == 0).ToList();
            List<LopMonHocDTO> danhSachLopcaLe = danhSachLopMonHoc.Where(lop => lop.MonHoc.SoTinChiLyThuyet <=2 && (lop.KhaNangDuocChon % 2) != 0).ToList();
            List<LopMonHocDTO> danhSachLopHaiCa = danhSachLopMonHoc.Where(lop => lop.MonHoc.SoTinChiLyThuyet > 2).ToList();
            int countCaChan = danhSachLopCaChan.Count();
            int countCaLe = danhSachLopcaLe.Count();
            int countHaiCa = danhSachLopHaiCa.Count();
            int max = countCaChan > countCaLe ? countCaChan : countCaLe;
            for (int index = 0; index < max; index++)
            {
                if (index < countCaLe)
                {
                    danhSachKetQua.Add(danhSachLopcaLe[index]);
                }
                if (index < countCaChan)
                {
                    danhSachKetQua.Add(danhSachLopCaChan[index]);
                }
            }
            danhSachKetQua.AddRange(danhSachLopHaiCa);
            int sum = danhSachLopMonHoc.Count();
            int absolute = Math.Abs(countCaChan - countCaLe);
            row2Ca = (int)(0.5 * (countCaChan + countCaLe - absolute));
            row = absolute + row2Ca + countHaiCa;
            return danhSachKetQua;
        }

        private String KiemTraTrungGio(List<LopMonHocDTO> danhSachLopMonHoc)
        {
            string resultString = string.Empty;
            int count = danhSachLopMonHoc.Count;
            if (count > 1)
            {
                for (int index1 = 0; index1 < count - 1; index1++)
                {
                    var lopMonHoc = danhSachLopMonHoc[index1];
                    for (int index2 = 1; index2 < count; index2++)
                    {
                        var lopMonHoc2 = danhSachLopMonHoc[index2];
                        if (lopMonHoc.LopMonHocId != lopMonHoc2.LopMonHocId && (lopMonHoc.MaGiangVien1 == lopMonHoc2.MaGiangVien1 || lopMonHoc.LopSinhVienId == lopMonHoc2.LopSinhVienId))
                        {
                            if (lopMonHoc.MonHoc.SoTinChiLyThuyet > 2 || lopMonHoc2.MonHoc.SoTinChiLyThuyet > 2)
                                resultString += lopMonHoc.MaLopVaTenMonHoc + " không thể trùng giờ với lớp " + lopMonHoc2.MaLopVaTenMonHoc + "<br />";
                            else
                            {
                                if (lopMonHoc.KhaNangDuocChon == lopMonHoc2.KhaNangDuocChon)
                                    resultString += lopMonHoc.MaLopVaTenMonHoc + " không thể trùng giờ với lớp " + lopMonHoc2.MaLopVaTenMonHoc + "<br />";
                            }
                        }
                    }
                }
            }
            return resultString;
        }

        public void LuuKetQua(string lopMonHocIds, string khaNangs)
        {
            List<LopMonHocDTO> danhSachLopMonHoc = _lopMonHocRepository.GetLopMonHocByIdList(lopMonHocIds).ToList();
            string[] khaNangList = khaNangs.Split(',');
            if (danhSachLopMonHoc.Count() != khaNangList.Count())
                throw new DataMisalignedException("Dữ liệu không hợp lệ.");
            int countLMH = danhSachLopMonHoc.Count();
            for (int subIndex = 0; subIndex < countLMH; subIndex++)
            {
                var lopMonHoc = danhSachLopMonHoc[subIndex];
                if (String.IsNullOrEmpty(khaNangList[subIndex]))
                {
                    lopMonHoc.MaTietDau = null;
                    lopMonHoc.MaTietSau = null;
                    lopMonHoc.Thu = 0;
                }
                else
                {
                    lopMonHoc.KhaNangDuocChon = Convert.ToInt32(khaNangList[subIndex]);
                    lopMonHoc = _lopMonHocCommandService.XepLich(lopMonHoc, lopMonHoc.KhaNangDuocChon);
                }
                _lopMonHocCommandService.UpdateLopMonHoc(lopMonHoc);
            }
        }
    }
}
